/*
 * Copyright (C) 1995, 1996, 1997 Wolfgang Solfrank
 * Copyright (c) 1995 Martin Husemann
 * Some structure declaration borrowed from Paul Popelka
 * (paulp@uts.amdahl.com), see /sys/msdosfs/ for reference.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *    This product includes software developed by Martin Husemann
 *    and Wolfgang Solfrank.
 * 4. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <sys/cdefs.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <stdio.h>
#include <unistd.h>
#include <time.h>
#include <sys/param.h>
#include <stdarg.h>
#include "fstool_common.h"
#include "fstool_io.h"
#include "fsck_fragment.h"
#include "fsck_cache.h"
#include "fsck_fat.h"
#include "fsck_dir.h"

#define DIR_ATTR_FILE_EMPTY     (0x00)        /* file no used */
#define DIR_ATTR_FILE_E5        (0x05)        /* file used is 0xe5 */
#define DIR_ATTR_FILE_DELETED   (0xe5)        /* file  deleted */
#define DIR_ATTR_VOLUME         (0x08)        /* volume entry label */
#define DIR_ATTR_DIRECTORY      (0x10)        /* directory entry name */
#define DIR_ATTR_LONG_NAME      (0x0f)        /* this is long name */
#define DIR_THISMOD             (0x8000)      /* just used for routine */

#define DIR_ENTRY_EMPTY         (1)
#define DIR_ENTRY_DELETED       (2)
#define DIR_ENTRY_SIZE          (32)

/** Global variables **/
static char s_aszLName[FSCK_DIR_LN_LEN_MAX] = "";
static u_char* s_pszClusterData = NULL;
static u_char* s_pszClusterRepairData = NULL;
static FSCK_DIR_ENTRY_S *s_pstRootDirList=NULL;
static FSCK_DIR_BAK_NODE_S *s_pstFreeBakDir=NULL;
static FSCK_DIR_BAK_NODE_S *s_pstPendingDir = NULL;
static FSCK_DIR_ENTRY_S *s_pstTmpDirEntry= NULL;

static u_char  dir_dot_header[16]={0x2E, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x10, 0x00, 0x00, 0x00, 0x00};
static u_char  dir_dot_dot_header[16]={0x2E, 0x2E, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x10, 0x00, 0x00, 0x00, 0x00};

static FSCK_DIR_ENTRY_S* Dir_CreateEntry(void)
{
    struct FSCK_DIR_ENTRY_S *de = NULL;
    if (!(de = s_pstTmpDirEntry))
    {
        if (!(de = (struct FSCK_DIR_ENTRY_S *)malloc(sizeof *de)))
            return 0;
    }
    else
    {
        s_pstTmpDirEntry = de->next;
    }
    return de;
}

static void Dir_FreeEntry(struct FSCK_DIR_ENTRY_S *de)
{
    de->next = s_pstTmpDirEntry;
    s_pstTmpDirEntry = de;

    if (s_pstTmpDirEntry)
    {
        free(s_pstTmpDirEntry);
        s_pstTmpDirEntry = NULL;
    }
}

static  FSCK_DIR_BAK_NODE_S* Dir_CreateBakNodeList(void)
{
    FSCK_DIR_BAK_NODE_S* dt = NULL;

    if (!(dt = s_pstFreeBakDir))
    {
        if (!(dt = (struct FSCK_DIR_BAK_NODE_S *)malloc(sizeof *dt)))
        {
            return 0;
        }
    }
    else
    {
        s_pstFreeBakDir = dt->next;
    }

    return dt;
}

static void Dir_FreeBakNodeList(FSCK_DIR_BAK_NODE_S *dt)
{
    dt->next = s_pstFreeBakDir;
    s_pstFreeBakDir = dt;

    if (s_pstFreeBakDir)
    {
        free(s_pstFreeBakDir);
        s_pstFreeBakDir = NULL;
    }
}

static char* Dir_GetPathName(FSCK_DIR_ENTRY_S *dir)
{
    static char namebuf[MAXPATHLEN + 1] = {0};
    char *cp = NULL;
    char *np = NULL;
    int nl = 0;
    errno_t err;
    cp = namebuf + sizeof(namebuf) - 1;
    *cp = '\0';
    do {
        if (dir->lname[0])
        {
            dir->lname[FSCK_DIR_LN_LEN_MAX - 1] = '\0';
            np = dir->lname;
        }
        else
        {
            dir->name[FSCK_DIR_NAME_LEN_MAX - 1] = '\0';
            np = dir->name;
        }
        nl = strlen(np);
        if ((cp -= nl) <= namebuf + 1)
            break;
        err = memcpy_s(cp, nl, np, nl);
        if(err != EOK)
        {
            FSTOOL_COMM_LOG_ERROR("memcpy_s failed, err = %d\n", err);
            return NULL;
        }
        *--cp = '/';
    } while ((dir = dir->parent) != NULL);
    if (dir)
        *--cp = '?';
    else
        cp++;
    return cp;
}

static u_char Dir_GetChkSum(u_char *p)
{
    u_char sum = 0;
    int i = 0;
    for (i = 0; i < 11; i++)
    {
        sum = (sum << 7)|(sum >> 1);
        sum += p[i];
    }
    return sum;
}

static u_int Dir_DelEntry(int fd, FSCK_BOOT_SECTOR_S *pstBoot, FSCK_CACHE_CLUSTER_CHAIN_S *fat, cl_t startcl,
    int startoff, cl_t endcl, int endoff, int notRepairLastCl)
{
    u_char* pStartAddr = NULL;
    u_char* pEndAddr = NULL;
    loff_t off = 0;
    int clsz = pstBoot->BPB_SecPerClus;
    struct FSCK_CACHE_S* pstTempCache = NULL;

    FSTOOL_COMM_LOG_DEBUG("Dir_DelEntry: %u:%u -->> %u:%u\n",startcl,startoff,endcl,endoff);

    pStartAddr = s_pszClusterRepairData + startoff;
    pEndAddr = s_pszClusterRepairData + clsz * pstBoot->BPB_BytesPerSec;
    if(!fat)
    {
        FSTOOL_COMM_LOG_ERROR("Can't find tmpCluster %d in cluster chain\n",startcl);
        return FSCK_FSFATAL;
    }

    while (startcl >= FSTOOL_COMM_CLUSTER_FIRST && startcl < pstBoot->NumClusters)
    {
        if (startcl == endcl)
        {
            if (notRepairLastCl)
            {
                break;
            }

            pEndAddr = s_pszClusterRepairData + endoff;
        }

        off = (loff_t)startcl * pstBoot->BPB_SecPerClus + pstBoot->StartClusSecNo;
        if (FSCK_FSOK != FSTOOL_IO_Read(fd, off, clsz, s_pszClusterRepairData))
        {
            FSTOOL_COMM_LOG_ERROR("Unable to read directory");
            return FSCK_FSFATAL|FSCK_READERR;
        }
        while (pStartAddr < pEndAddr)
        {
            *pStartAddr = DIR_ATTR_FILE_DELETED;
            pStartAddr += DIR_ENTRY_SIZE;
        }

        if (FSCK_FSOK != FSTOOL_IO_Write(fd, off, clsz, s_pszClusterRepairData))
        {
            FSTOOL_COMM_LOG_ERROR("Unable to write directory");
            return FSCK_FSFATAL|FSCK_WRITERR;
        }

        if (startcl == endcl)
        {
            break;
        }

        pstTempCache = FSCK_CACHE_Next(fat,startcl,&startcl);
        if(!pstTempCache && !startcl)
        {
            FSTOOL_COMM_LOG_ERROR("[%s][%d] fialed\n",__func__,__LINE__);
            return FSCK_FSFATAL;
        }
        pStartAddr = s_pszClusterRepairData;
    }
    return FSCK_FSOK;
}

/*lint -e613*/
static u_int Dir_RemoveEntry(int f, FSCK_BOOT_SECTOR_S* pstBoot, FSCK_CACHE_CLUSTER_CHAIN_S* fat, u_char* pStartOff,
    u_char* pEndOff, cl_t startcl, cl_t endcl, cl_t curcl, char* path)
{
    u_int ret = FSCK_FSOK;
    FSTOOL_COMM_LOG_WARN("Dir_RemoveEntry:[name:%s]\n"
                         "[chain head:%u, cur:%u, end:%u]\n"
                         "[pStartOff:%u, pEndOff:%u]\n",
                          path, startcl, curcl, endcl,
                          (unsigned int)(pStartOff - s_pszClusterData), (unsigned int)(pEndOff - s_pszClusterData));

    if (startcl != curcl)
    {
        ret = Dir_DelEntry(f, pstBoot, fat, startcl, pStartOff - s_pszClusterData,
               endcl, pEndOff - s_pszClusterData, endcl == curcl);
        if (FSCK_FSOK != ret)
        {
            return ret;
        }
        pStartOff = s_pszClusterData;
    }

    if (endcl == curcl)
    {
        for (; pStartOff < pEndOff; pStartOff += DIR_ENTRY_SIZE)
        {
            *pStartOff = DIR_ATTR_FILE_DELETED;
        }
    }

    return FSCK_FSDIRMOD;
}
/*lint +e613*/

static u_int Dir_CheckFileSize(FSCK_BOOT_SECTOR_S *pstBoot, u_char* pDEBuf, FSCK_CACHE_CLUSTER_CHAIN_S* pstChain, FSCK_DIR_ENTRY_S* pstDE, HI_FSTOOL_CHECK_MODE_E enMode)
{
    FSCK_CACHE_S* pstCache = NULL;
    u_int64 physicalSize = 0;
    cl_t tmpCluster = 0;
    u_int sz = 0;

    physicalSize = ((u_int64)pstChain->length) * ((u_int64)pstBoot->ClusterSize);
    if (physicalSize > FSTOOL_COMM_FILE_SIZE_MAX)
    {
        FSTOOL_COMM_LOG_ERROR("The file %s physical size exceed 4G. %llu\n",Dir_GetPathName(pstDE), physicalSize);
        return FSCK_FSERR;
    }

    if ((HI_FSTOOL_CHECK_MODE_ALWAYS_MAIN_FAT == enMode)
        || (HI_FSTOOL_CHECK_MODE_DIRTY_MAIN_FAT == enMode))
    {
        if ((physicalSize < (u_int64_t)pstDE->size)
            || (physicalSize >= (u_int64_t)(pstBoot->ClusterSize + pstDE->size)))
        {
            FSTOOL_COMM_LOG_WARN("File:%s, clusterChainSize:%llu, dirSize:%u, dirHead:%u\n",Dir_GetPathName(pstDE), physicalSize, pstDE->size, pstDE->head);
            pstDE->size = physicalSize;
            pDEBuf[28] = (u_char)physicalSize;
            pDEBuf[29] = (u_char)(physicalSize >> 8);
            pDEBuf[30] = (u_char)(physicalSize >> 16);
            pDEBuf[31] = (u_char)(physicalSize >> 24);
            return FSCK_FSDIRMOD;
        }
    }
    else
    {
        //modify size info in dir entry
        if (physicalSize < (u_int64_t)pstDE->size)
        {
            FSTOOL_COMM_LOG_WARN("File:%s, clusterChainSize:%llu, dirSize:%u, dirHead:%u\n",Dir_GetPathName(pstDE), physicalSize, pstDE->size, pstDE->head);
            pstDE->size = physicalSize;
            pDEBuf[28] = (u_char)physicalSize;
            pDEBuf[29] = (u_char)(physicalSize >> 8);
            pDEBuf[30] = (u_char)(physicalSize >> 16);
            pDEBuf[31] = (u_char)(physicalSize >> 24);
            return FSCK_FSDIRMOD;
        }
        else if (physicalSize >= (u_int64_t)(pstBoot->ClusterSize + pstDE->size))
        {
            //trunc the cluster chain form tail to head till the size is same with the dir entry
            FSTOOL_COMM_LOG_WARN("File:%s, clusterChainSize:%llu, dirSize:%u, dirHead:%u\n",Dir_GetPathName(pstDE), physicalSize, pstDE->size, pstDE->head);
            for (tmpCluster = pstDE->head; (sz += pstBoot->ClusterSize) < pstDE->size;)
            {
                pstCache = FSCK_CACHE_Next(pstChain, tmpCluster, &tmpCluster);
                if(!tmpCluster)
                {
                    return FSCK_FSERR;
                }

                if (FAT_IsEOFCluster(tmpCluster))
                {
                    break;
                }
            }

            if(!pstCache && !tmpCluster)
            {
                return FSCK_FSERR;
            }
            FSCK_CACHE_Trunc(pstBoot,pstChain,tmpCluster);
            FSTOOL_COMM_LOG_DEBUG(" Ation After truncate ,pstChain->length = %d \n",pstChain->length);
            return FSCK_FSFATMOD;
        }
    }
    return FSCK_FSOK;
}

static int Dir_CheckDotDot(int f, FSCK_BOOT_SECTOR_S *pstBoot, FSCK_DIR_ENTRY_S *dir)
{
    u_char *p = NULL;
    u_char *buf = NULL;
    loff_t off = 0;
    size_t last = 0;
    cl_t tmpCluster = 0;
    int rc=0;
    int n_count = 0;
    struct FSCK_CACHE_CLUSTER_CHAIN_S *fat =NULL;
    struct FSCK_CACHE_CLUSTER_CHAIN_S tofind = {0};
    struct FSCK_CACHE_S *cache = NULL;
    int dot = 0;
    int dotdot = 0;
    dot = dotdot = 0;
    tmpCluster = dir->head;
    if (dir->parent && !FAT_IsValidCluster(pstBoot,tmpCluster))
    {
        return rc;
    }
    tofind.head = dir->head;
    fat = FSCK_CACHE_FindChain(&g_stAllocatedCluster, &tofind);
    if(!fat)
    {
        FSTOOL_COMM_LOG_ERROR("Can not find cluster chain [head:%u]\n",dir->head);
        return FSCK_FSFATAL;
    }
    do {
        last = pstBoot->BPB_SecPerClus;
        off = (loff_t)tmpCluster * pstBoot->BPB_SecPerClus + pstBoot->StartClusSecNo;
        buf = malloc(last * pstBoot->BPB_BytesPerSec);
        if (!buf)
        {
            FSTOOL_COMM_LOG_ERROR("Unable to malloc");
            return FSCK_FSFATAL|FSCK_OOM;
        }

        if (FSCK_FSOK != FSTOOL_IO_Read(f, off, last, buf))
        {
            FSTOOL_COMM_LOG_ERROR("Unable to read");
            free(buf);
            return FSCK_FSFATAL|FSCK_READERR;
        }

        last /= DIR_ENTRY_SIZE;
        p = buf;
        for (n_count=0, rc=0; n_count < 11; n_count++)
        {
            if (dir_dot_header[n_count] != p[n_count])
            {
                rc=-1;
                break;
            }
        }
        if(!rc)
        {
            dot=1;
        }

        for (n_count = 0, rc = 0; n_count < 11; n_count++)
        {
            if (dir_dot_dot_header[n_count] != p[n_count+DIR_ENTRY_SIZE])
            {
                rc=-1;
                break;
            }
        }
        if(!rc)
        {
            dotdot=1;
        }
        free(buf);
        buf = NULL;
        cache = FSCK_CACHE_Next(fat,tmpCluster,&tmpCluster);
        if(!cache && !tmpCluster)
        {
            FSTOOL_COMM_LOG_ERROR("[%s][%d] fialed\n",__func__,__LINE__);
            return FSCK_FSFATAL;
        }
    } while (FAT_IsValidCluster(pstBoot, tmpCluster));

    if (!dot || !dotdot)
    {
        if (!dot)
            FSTOOL_COMM_LOG_WARN("dir%s: '.' absent for %s.\n",__func__,dir->name);

        if (!dotdot)
            FSTOOL_COMM_LOG_WARN("dir%s: '..' absent for %s. \n",__func__,dir->name);
        return -1;
    }
    return 0;
}

static void Dir_SetShortEntry(u_char* pTempBuf, FSCK_DIR_ENTRY_S* pstDirEnt, FSCK_DIR_ENTRY_S* pstParentDirEnt)
{
    int i = 0;
    int j = 0;

    for (i = 0; i < 8; i++)
    {
        pstDirEnt->name[i] = pTempBuf[i];
    }
    pstDirEnt->name[8] = '\0';

    for (j = 7; j >= 0 && pstDirEnt->name[j] == ' '; j--)
    {
        pstDirEnt->name[j] = '\0';
    }

    j++;

    if (pstDirEnt->name[0] == DIR_ATTR_FILE_E5)
    {
        pstDirEnt->name[0] = 0xe5;  /*lint !e569*/
    }

    if (pTempBuf[8] != ' ')
    {
        pstDirEnt->name[j++] = '.';
    }

    for (i = 0; i < 3; i++)
    {
        pstDirEnt->name[j++] = pTempBuf[i+8];
    }

    pstDirEnt->name[j] = '\0';

    for (j--; j >= 0 && pstDirEnt->name[j] == ' '; j--)
    {
        pstDirEnt->name[j] = '\0';
    }

    pstDirEnt->head = pTempBuf[26] | (pTempBuf[27] << 8);
    pstDirEnt->head |= (pTempBuf[20] << 16) | (pTempBuf[21] << 24);
    pstDirEnt->size = pTempBuf[28] | (pTempBuf[29] << 8) | (pTempBuf[30] << 16) | (pTempBuf[31] << 24);
    pstDirEnt->parent = pstParentDirEnt;
    pstDirEnt->next = pstParentDirEnt->child;
}

/*lint -e570 -e613 -e78 -e40 -e63*/
static u_int Dir_GetSection(int f, FSCK_BOOT_SECTOR_S* pstBoot, FSCK_DIR_ENTRY_S* dir, HI_FSTOOL_CHECK_MODE_E enMode)
{
    FSCK_DIR_ENTRY_S dirent = {0};
    FSCK_DIR_ENTRY_S* pstCurDE = NULL;
    FSCK_DIR_BAK_NODE_S* pstDENode = NULL;

    FSCK_CACHE_CLUSTER_CHAIN_S* pstCurDirChain = NULL;
    FSCK_CACHE_CLUSTER_CHAIN_S* pstCurDEChain = NULL;
    FSCK_CACHE_CLUSTER_CHAIN_S tofind = {0};
    FSCK_CACHE_S* cache = NULL;

    u_char* pTempBuf = NULL;
    u_char* pStartOfValidLNE = NULL;//start address of valid long name dir entry
    u_char* pStartOfInvalidLNE = NULL;//start address of invalid long name dir entry
    u_char* pEmpEntry = NULL;
    loff_t off = 0;
    int i = 0;
    int k = 0;
    int last = 0;
    cl_t tmpCluster = ~0;
    cl_t validLNECL = ~0;//the cluster of valid long name dir entry
    cl_t invalidLNECl = ~0;//the cluster of start invalid long name dir entry
    cl_t empCluster = ~0;
    char *t = NULL;
    u_int longNameEntryID = 0;
    int dirChkSum = -1;
    u_int mod = FSCK_FSOK;
    u_int ret = 0;
    u_char* pEntryToDel = NULL;

    tmpCluster = dir->head;
    if (dir->parent && !FAT_IsValidCluster(pstBoot, tmpCluster))
    {
        return FSCK_FSOK;
    }

    tofind.head = dir->head;
    pstCurDirChain = FSCK_CACHE_FindChain(&g_stAllocatedCluster, &tofind);
    if(!pstCurDirChain)
    {
        FSTOOL_COMM_LOG_ERROR("%s:can not find cluster chain(head = %u)\n",__func__,dir->head);
        return FSCK_FSFATMOD;
    }

    do
    {
        pstCurDEChain = NULL;

        last = pstBoot->BPB_BytesPerSec * pstBoot->BPB_SecPerClus;
        off = ((loff_t)tmpCluster * pstBoot->BPB_SecPerClus + pstBoot->StartClusSecNo) * pstBoot->BPB_BytesPerSec;
        (void)memset_s(s_pszClusterData, (size_t)(pstBoot->BPB_SecPerClus * pstBoot->BPB_BytesPerSec), 0x00, last);

        if (FSCK_FSOK != FSTOOL_IO_Read(f, off / pstBoot->BPB_BytesPerSec, last / pstBoot->BPB_BytesPerSec, s_pszClusterData)) /*lint !e573*/
        {
            FSTOOL_COMM_LOG_ERROR("Unable to read");
            return FSCK_FSFATAL|FSCK_READERR;
        }

        last /= DIR_ENTRY_SIZE;
        for (pTempBuf = s_pszClusterData, i = 0; i < last; i++, pTempBuf += DIR_ENTRY_SIZE)  /*lint !e574*/
        {
            if (dir->state & DIR_ENTRY_DELETED)
            {
                *pTempBuf = DIR_ATTR_FILE_EMPTY;
                continue;
            }

            if (*pTempBuf == DIR_ATTR_FILE_EMPTY)
            {
                dir->state |= DIR_ENTRY_EMPTY;
                pEmpEntry = pTempBuf;
                empCluster = tmpCluster;
                continue;
            }

            //modify empty entry to deleted until used entry
            if (dir->state & DIR_ENTRY_EMPTY)
            {
                FSTOOL_COMM_LOG_WARN("%s has entries after end of directory\n",Dir_GetPathName(dir));
                ret = Dir_DelEntry(f, pstBoot, pstCurDirChain,
                       empCluster, pEmpEntry - s_pszClusterData,
                       tmpCluster, pTempBuf - s_pszClusterData, 1);
                if (FSCK_FSOK != ret)
                {
                    return ret;
                }

                if (empCluster == tmpCluster)
                {
                    pEntryToDel = pEmpEntry;
                }
                else
                {
                    pEntryToDel = s_pszClusterData;
                }

                for (; pEntryToDel < pTempBuf; pEntryToDel += DIR_ENTRY_SIZE)
                {
                    *pEntryToDel = DIR_ATTR_FILE_DELETED;
                }

                pEmpEntry = NULL;
                mod |= DIR_THISMOD|FSCK_FSDIRMOD;
                dir->state &= ~DIR_ENTRY_EMPTY;
            }

            if (*pTempBuf == DIR_ATTR_FILE_DELETED)
            {
                continue;
            }

            //check long name dir entry
            if (pTempBuf[11] == DIR_ATTR_LONG_NAME)
            {
                if (*pTempBuf & FSCK_DIR_LNR_1ST_MARK)
                {
                    if (dirChkSum != -1)
                    {
                        if (!pStartOfInvalidLNE)
                        {
                            pStartOfInvalidLNE = pStartOfValidLNE;
                            invalidLNECl = validLNECL;
                        }
                    }

                    (void)memset_s(s_aszLName, sizeof s_aszLName, 0, sizeof s_aszLName);
                    dirChkSum = pTempBuf[13];
                    pStartOfValidLNE = pTempBuf;
                    validLNECL = tmpCluster;
                }
                else if (dirChkSum != pTempBuf[13] || longNameEntryID != (*pTempBuf & FSCK_DIR_LNR_SEQ_NUM_MASK))
                {
                    FSTOOL_COMM_LOG_WARN("dirChkSum or lidx error,[cluster:%u, absolute offset:%llu]\n",tmpCluster,  (u_int64)(off + pTempBuf - s_pszClusterData)); /*lint !e571*/
                    if (!pStartOfInvalidLNE)
                    {
                        pStartOfInvalidLNE = pStartOfValidLNE;
                        invalidLNECl = validLNECL;
                    }
                    if (!pStartOfInvalidLNE)
                    {
                        pStartOfInvalidLNE = pTempBuf;
                        invalidLNECl = tmpCluster;
                    }
                    pStartOfValidLNE = NULL;
                }

                longNameEntryID = *pTempBuf & FSCK_DIR_LNR_SEQ_NUM_MASK;
                t = s_aszLName + --longNameEntryID * 13;
                for (k = 1; k < 11 && t < s_aszLName + sizeof(s_aszLName); k += 2)
                {
                    if (!pTempBuf[k] && !pTempBuf[k + 1])
                    {
                        break;
                    }
                    *t++ = pTempBuf[k];
                    if (pTempBuf[k + 1])
                    {
                        t[-1] = '?';
                    }
                }
                if (k >= 11)
                {
                    for (k = 14; k < 26 && t < s_aszLName + sizeof(s_aszLName); k += 2)
                    {
                        if (!pTempBuf[k] && !pTempBuf[k + 1])
                            break;
                        *t++ = pTempBuf[k];
                        if (pTempBuf[k + 1])
                            t[-1] = '?';
                    }
                }
                if (k >= 26)
                {
                    for (k = 28; k < DIR_ENTRY_SIZE && t < s_aszLName + sizeof(s_aszLName); k += 2)
                    {
                        if (!pTempBuf[k] && !pTempBuf[k + 1])
                            break;
                        *t++ = pTempBuf[k];
                        if (pTempBuf[k + 1])
                            t[-1] = '?';
                    }
                }

                if (t >= s_aszLName + sizeof(s_aszLName))
                {
                    FSTOOL_COMM_LOG_WARN("long filename too long,[cluster:%u, absolute offset:%llu]\n",tmpCluster,  (u_int64)(off + pTempBuf - s_pszClusterData));   /*lint !e571*/
                    if (!pStartOfInvalidLNE)
                    {
                        pStartOfInvalidLNE = pStartOfValidLNE;
                        invalidLNECl = validLNECL;
                    }
                    pStartOfValidLNE = NULL;
                }

                if (pTempBuf[26] | pTempBuf[27])
                {
                    FSTOOL_COMM_LOG_WARN("long filename record cluster start != 0,[cluster:%u, absolute offset:%llu]\n",
                                         tmpCluster,  (u_int64)(off + pTempBuf - s_pszClusterData));     /*lint !e571*/
                    if (!pStartOfInvalidLNE)
                    {
                        pStartOfInvalidLNE = pStartOfValidLNE;
                        invalidLNECl = validLNECL;
                    }
                    pStartOfValidLNE = NULL;
                }
                continue;
            }

            (void)memset_s(&dirent, sizeof dirent, 0, sizeof dirent);
            dirent.flags = pTempBuf[11];
            if (dirent.flags & DIR_ATTR_VOLUME)
            {
                //volume should not be a long dir antry
                if (pStartOfValidLNE || pStartOfInvalidLNE)
                {
                    FSTOOL_COMM_LOG_WARN("DIR_ATTR_VOLUME,[cluster:%u, absolute offset:%llu\n",tmpCluster, (u_int64)(off + pTempBuf - s_pszClusterData));    /*lint !e571*/
                    mod |= Dir_RemoveEntry(f, pstBoot, pstCurDirChain,
                            pStartOfInvalidLNE ? pStartOfInvalidLNE : pStartOfValidLNE,
                            pTempBuf,
                            pStartOfInvalidLNE ? invalidLNECl : validLNECL,
                            -1, 0, Dir_GetPathName(dir));    /*lint !e570*/
                    pStartOfValidLNE = NULL;
                    pStartOfInvalidLNE = NULL;
                }
                continue;
            }

            //check long name dir entry chksum
            if (pStartOfValidLNE && dirChkSum != Dir_GetChkSum(pTempBuf))
            {
                if (!pStartOfInvalidLNE)
                {
                    pStartOfInvalidLNE = pStartOfValidLNE;
                    invalidLNECl = validLNECL;
                }
                pStartOfValidLNE = NULL;
            }

            //set short entry
            Dir_SetShortEntry(pTempBuf, &dirent, dir);
            //set long name of dir entry
            if (pStartOfValidLNE)
            {
                (void)snprintf_s(dirent.lname, FSCK_DIR_LN_LEN_MAX, strlen(s_aszLName), "%s", s_aszLName);   /*lint !e534*/
                s_aszLName[0] = '\0';
                dirChkSum = -1;
            }

            //if this long name dir entry is err, remove this long name dir entry, but short entry will not remove
            if (pStartOfInvalidLNE)
            {
                if (pStartOfValidLNE)
                {
                    /*has valid long name dir before short dir entry*/
                    /*remove the invalid long name dir entry before the valid long name dir entry*/
                    mod |= k = Dir_RemoveEntry(f, pstBoot, pstCurDirChain,
                                pStartOfInvalidLNE, pStartOfValidLNE,
                                invalidLNECl, validLNECL, tmpCluster,
                                Dir_GetPathName(&dirent));

                    if (mod & FSCK_FSFATAL)
                    {
                        FSTOOL_COMM_LOG_ERROR("[%s][%d] fialed\n",__func__,__LINE__);
                        return mod;
                    }

                    if ((validLNECL == tmpCluster) && (pStartOfValidLNE != s_pszClusterData) && (k & FSCK_FSDIRMOD))
                    {
                        mod |= DIR_THISMOD;
                    }
                }
                else
                {
                    /*has no valid long name dir entry before short dir entry*/
                    /*remove the invalid long name dir entry before the short dir entry*/
                    mod |= k = Dir_RemoveEntry(f, pstBoot, pstCurDirChain,
                                pStartOfInvalidLNE, pTempBuf,
                                invalidLNECl, tmpCluster, tmpCluster,
                                Dir_GetPathName(&dirent));

                    if (mod & FSCK_FSFATAL)
                    {
                        FSTOOL_COMM_LOG_ERROR("[%s][%d] fialed\n",__func__,__LINE__);
                        return mod;
                    }

                    if ((pTempBuf != s_pszClusterData) && (k & FSCK_FSDIRMOD))
                    {
                        mod |= DIR_THISMOD;
                    }
                }
            }

            pstCurDEChain = NULL;
            //there's no need to find cluster of "." and ".."
            if (FAT_IsValidCluster(pstBoot, dirent.head)
                && strncmp(dirent.name, ".", FSCK_DIR_NAME_LEN_MAX)
                && strncmp(dirent.name, "..", FSCK_DIR_NAME_LEN_MAX))
            {
                tofind.head = dirent.head;
                pstCurDEChain = FSCK_CACHE_FindChain(&g_stAllocatedCluster, &tofind);
                if (pstCurDEChain)
                {
                    pstCurDEChain->flag |= FSTOOL_COMM_CLUSTER_CHAIN_USED;
                }
                else
                {
                    FSTOOL_COMM_LOG_WARN("can't find cluster chain head[%u] of file[%s]\n",dirent.head,Dir_GetPathName(&dirent));
                    if (pStartOfValidLNE)
                    {
                        mod |= Dir_RemoveEntry(f, pstBoot, pstCurDirChain,
                                               pStartOfValidLNE, pTempBuf,
                                               validLNECL, tmpCluster, tmpCluster,
                                               Dir_GetPathName(dir));
                    }

                    *pTempBuf = DIR_ATTR_FILE_DELETED;
                    mod |= DIR_THISMOD|FSCK_FSDIRMOD;
                }
            }

            pStartOfValidLNE = NULL; /* not used any longer */
            pStartOfInvalidLNE = NULL;

            if (dirent.flags & DIR_ATTR_DIRECTORY)
            {
                //modify the dir size to 0
                if (dirent.size)
                {
                    FSTOOL_COMM_LOG_WARN("Directory %s has size != 0\n",Dir_GetPathName(&dirent));
                    pTempBuf[28] = pTempBuf[29] = pTempBuf[30] = pTempBuf[31] = 0;
                    dirent.size = 0;
                    mod |= DIR_THISMOD|FSCK_FSDIRMOD;
                }

                if (strncmp(dirent.name, ".", FSCK_DIR_NAME_LEN_MAX) == 0)
                {
                    //keep head of dir entry same with self record in father entry
                    if (dirent.head != dir->head)
                    {
                        FSTOOL_COMM_LOG_WARN("'.' entry in %s has incorrect start cluster\n", Dir_GetPathName(dir));
                        dirent.head = dir->head;
                        pTempBuf[27] = (u_char)(dirent.head >> 8);
                        pTempBuf[26] = (u_char)dirent.head;
                        pTempBuf[21] = (u_char)(dirent.head >> 24);
                        pTempBuf[20] = (u_char)(dirent.head >> 16);
                        mod |= DIR_THISMOD|FSCK_FSDIRMOD;
                    }
                    continue;
                }
                else if (strncmp(dirent.name, "..", FSCK_DIR_NAME_LEN_MAX) == 0)
                {
                    if (dir->parent)
                    {
                        if (!dir->parent->parent)
                        {
                            if (dirent.head)
                            {
                                FSTOOL_COMM_LOG_WARN("'..' entry in %s has non-zero start cluster\n", Dir_GetPathName(dir));
                                dirent.head = 0;
                                pTempBuf[26] = pTempBuf[27] = 0;
                                pTempBuf[20] = pTempBuf[21] = 0;
                                mod |= DIR_THISMOD|FSCK_FSDIRMOD;
                            }
                        }
                        //keep head of father dir entry same with record in grandfather entry
                        else if (dirent.head != dir->parent->head)
                        {
                            FSTOOL_COMM_LOG_WARN("'..' entry in %s has incorrect start cluster\n",Dir_GetPathName(dir));
                            dirent.head = dir->parent->head;
                            pTempBuf[27] = (u_char)(dirent.head >> 8);
                            pTempBuf[26] = (u_char)dirent.head;
                            pTempBuf[21] = (u_char)(dirent.head >> 24);
                            pTempBuf[20] = (u_char)(dirent.head >> 16);
                            mod |= DIR_THISMOD|FSCK_FSDIRMOD;
                        }
                    }
                    continue;
                }
                else
                {
                    //if start cluster of directory is invalid or is equal to it's father's start cluster, the dir entry is invalid and rm it
                    if (!FAT_IsValidCluster(pstBoot, dirent.head) || (dirent.head == dir->head))
                    {
                        FSTOOL_COMM_LOG_WARN("[%s] entry in [%s] has incorrect start cluster[%u], and remove this dir entry\n",
                                dirent.name, Dir_GetPathName(dir), dirent.head);
                        *pTempBuf = DIR_ATTR_FILE_DELETED;
                        mod |= DIR_THISMOD|FSCK_FSDIRMOD;

                        if (pstCurDEChain)
                        {
                            pstCurDEChain->flag &= ~FSTOOL_COMM_CLUSTER_CHAIN_USED;
                        }

                        continue;
                    }

                    if (Dir_CheckDotDot(f,pstBoot,&dirent))
                    {
                        *pTempBuf = DIR_ATTR_FILE_DELETED;
                        mod |= DIR_THISMOD|FSCK_FSDIRMOD;
                        if (pstCurDEChain)
                        {
                            pstCurDEChain->flag &= ~FSTOOL_COMM_CLUSTER_CHAIN_USED;
                        }
                        continue;
                    }
                }

                //if the dir entry is valid, then new one entry and add to father entry
                if (!(pstCurDE = Dir_CreateEntry()))
                {
                    FSTOOL_COMM_LOG_ERROR("No space for directory");
                    return FSCK_FSFATAL|FSCK_OOM;
                }
                (void)memcpy_s(pstCurDE, sizeof(FSCK_DIR_ENTRY_S), &dirent, sizeof(FSCK_DIR_ENTRY_S));
                dir->child = pstCurDE;

                pstDENode = Dir_CreateBakNodeList();
                if (!pstDENode)
                {
                    FSTOOL_COMM_LOG_ERROR("No space for todo list");
                    return FSCK_FSFATAL|FSCK_OOM;
                }
                pstDENode->next = s_pstPendingDir;
                pstDENode->dir = pstCurDE;
                s_pstPendingDir = pstDENode;
            }
            else
            {
                if (!pstCurDEChain)
                {
                    if (FAT_IsFreeCluster(dirent.head))
                    {
                        dirent.size = 0;
                        pTempBuf[28] = pTempBuf[29] = pTempBuf[30] = pTempBuf[31] = 0;
                        mod |= DIR_THISMOD|FSCK_FSDIRMOD;
                    }
                    else if (!FAT_IsValidCluster(pstBoot, dirent.head))
                    {
                        dirent.size = 0;
                        dirent.head = 0;
                        //*pTempBuf = DIR_ATTR_FILE_DELETED;
                        pTempBuf[20] = pTempBuf[21] = 0;
                        pTempBuf[26] = pTempBuf[27] = 0;
                        pTempBuf[28] = pTempBuf[29] = pTempBuf[30] = pTempBuf[31] = 0;
                        mod |= DIR_THISMOD|FSCK_FSDIRMOD;
                    }

                    continue;
                }

                mod |= k = Dir_CheckFileSize(pstBoot, pTempBuf, pstCurDEChain, &dirent, enMode);
                if (k & FSCK_FSDIRMOD)
                {
                    mod |= DIR_THISMOD;
                }
                else if (k & FSCK_FSERR)
                {
                    FSTOOL_COMM_LOG_ERROR("[%s][%d] fialed\n",__func__,__LINE__);
                    return mod|FSCK_FSFATAL;
                }
            }
            pstBoot->NumFiles++;
        }

        //write modified dir entry to partition
        if (mod & DIR_THISMOD)
        {
            last *= DIR_ENTRY_SIZE;
            if (FSCK_FSOK != FSTOOL_IO_Write(f, off / pstBoot->BPB_BytesPerSec, last / pstBoot->BPB_BytesPerSec, s_pszClusterData))  /*lint !e573*/
            {
                FSTOOL_COMM_LOG_ERROR("Unable to write directory");
                return FSCK_FSFATAL|FSCK_WRITERR;
            }
            mod &= ~DIR_THISMOD;
        }
        cache = FSCK_CACHE_Next(pstCurDirChain,tmpCluster,&tmpCluster);
        if(!cache && !tmpCluster)
        {
            FSTOOL_COMM_LOG_ERROR("%s :Find nextclus error \n",__func__);
            return FSCK_FSFATAL;
        }
    } while (FAT_IsValidCluster(pstBoot, tmpCluster));

    if (pStartOfInvalidLNE || pStartOfValidLNE)
    {
        mod |= Dir_RemoveEntry(f, pstBoot, pstCurDirChain,
                pStartOfInvalidLNE, pTempBuf,
                invalidLNECl, -1, 0,
                Dir_GetPathName(dir));
    }
    else if (pStartOfValidLNE)
    {
        mod |= Dir_RemoveEntry(f, pstBoot, pstCurDirChain,
                pStartOfValidLNE, pTempBuf,
                validLNECL, -1, 0,
                Dir_GetPathName(dir));
    }

    return mod & ~DIR_THISMOD;
}
/*lint +e570 +e613 +e78 +e40 +e63*/

u_int FSCK_DIR_Reset(FSCK_BOOT_SECTOR_S *pstBoot)
{
    struct FSCK_CACHE_CLUSTER_CHAIN_S* pstChain = NULL;
    struct FSCK_CACHE_CLUSTER_CHAIN_S tofind = {0};
    struct FSCK_CACHE_CLUSTER_CHAIN_S *insert = NULL;
    struct FSCK_FRAGMENT_S *frag = NULL;
    struct FSCK_FRAGMENT_S tofind_frag ={0};
    struct FSCK_CACHE_S *cache = NULL;

    if (!(s_pszClusterData = malloc((size_t)pstBoot->BPB_SecPerClus * pstBoot->BPB_BytesPerSec))
        || !(s_pszClusterRepairData = malloc((size_t)pstBoot->BPB_SecPerClus * pstBoot->BPB_BytesPerSec))
        || !(s_pstRootDirList = Dir_CreateEntry()))
    {
        FSTOOL_COMM_LOG_ERROR("No space for directory");
        return FSCK_FSFATAL|FSCK_OOM;
    }
    (void)memset_s(s_pstRootDirList, sizeof *s_pstRootDirList, 0, sizeof *s_pstRootDirList);

    if (pstBoot->BPB_RootClus < FSTOOL_COMM_CLUSTER_FIRST || pstBoot->BPB_RootClus >= pstBoot->NumClusters)
    {
        FSTOOL_COMM_LOG_ERROR("Root directory starts with cluster out of range(%u)", pstBoot->BPB_RootClus);
        return FSCK_FSFATAL;
    }

    tofind.head = pstBoot->BPB_RootClus;

    pstChain = FSCK_CACHE_FindChain(&g_stAllocatedCluster, &tofind);
    if(!pstChain)
    {
        FSTOOL_COMM_LOG_WARN("can not find Root directory in cluster chain\n");
        tofind_frag.head = pstBoot->BPB_RootClus;
        frag = FSCK_FRAGMENT_Find(&g_stFreeCluster, &tofind_frag);
        if(!frag)
        {
            FSTOOL_COMM_LOG_ERROR("can not find Root direntory in free chain\n");
            return FSCK_FSFATAL;
        }

        /*find it in free rb tree,now move it to cluster chain*/
        pstChain = FSCK_CACHE_NewChain();
        if(!pstChain)
        {
            FSTOOL_COMM_LOG_ERROR("[%s][%d] fialed\n",__func__,__LINE__);
            return FSCK_FSFATAL|FSCK_OOM;
        }

        pstChain->head = pstBoot->BPB_RootClus;
        pstChain->length = 1;
        cache = FSCK_CACHE_New();
        if(!cache)
        {
            free(pstChain);
            FSTOOL_COMM_LOG_ERROR("[%s][%d] fialed\n",__func__,__LINE__);
            return FSCK_FSFATAL|FSCK_OOM;
        }

        cache->head = pstBoot->BPB_RootClus;
        cache->length = 1;
        FSCK_CACHE_Add(pstChain,cache);

        insert = FSCK_CACHE_InsertChain(&g_stAllocatedCluster, pstChain);
        if (insert)
        {
            FSTOOL_COMM_LOG_ERROR("%s:fatentry(head:0x%x) exist\n",__func__,pstChain->head);
            free(cache);
            free(pstChain);
            return FSCK_FSFATAL;
        }

        FSCK_FRAGMENT_Remove(&g_stFreeCluster, frag);
        free(frag);
        return FSCK_FSFATMOD;
    }

    pstChain->flag |= FSTOOL_COMM_CLUSTER_CHAIN_USED;
    s_pstRootDirList->head = pstBoot->BPB_RootClus;
    return FSCK_FSOK;
}


void FSCK_DIR_Free(void)
{
    struct FSCK_DIR_BAK_NODE_S *p = NULL;
    struct FSCK_DIR_BAK_NODE_S *np = NULL;
    struct FSCK_DIR_ENTRY_S *d = NULL;
    struct FSCK_DIR_ENTRY_S *nd = NULL;

    for (p = s_pstPendingDir; p; p = np)
    {
        np = p->next;
        Dir_FreeBakNodeList(p);
    }
    s_pstPendingDir = 0;
    for (d = s_pstRootDirList; d; d = nd)
    {
        if ((nd = d->child) != NULL)
        {
            d->child = 0;
            continue;
        }
        if (!(nd = d->next))
            nd = d->parent;
        Dir_FreeEntry(d);
    }
    s_pstRootDirList = NULL;
    if (s_pszClusterData!=NULL)
    {
        free(s_pszClusterData);
        s_pszClusterData = NULL;
    }
    if (s_pszClusterRepairData!=NULL)
    {
        free(s_pszClusterRepairData);
        s_pszClusterRepairData = NULL;
    }
}

u_int FSCK_DIR_HandleTree(int fs, FSCK_BOOT_SECTOR_S* pstBoot, HI_FSTOOL_CHECK_MODE_E enMode)
{
    u_int mod = 0;
    mod = Dir_GetSection(fs, pstBoot, s_pstRootDirList, enMode);
    if (mod & FSCK_FSFATAL)
    {
        FSTOOL_COMM_LOG_ERROR("[%s][%d] fialed\n",__func__,__LINE__);
        return mod;
    }

    while (s_pstPendingDir)
    {
        FSCK_DIR_ENTRY_S* pstDE = s_pstPendingDir->dir;
        FSCK_DIR_BAK_NODE_S* pstNode = s_pstPendingDir->next;

        Dir_FreeBakNodeList(s_pstPendingDir);
        s_pstPendingDir = pstNode;

        mod |= Dir_GetSection(fs, pstBoot, pstDE, enMode);
        if (mod & FSCK_FSFATAL)
        {
            FSTOOL_COMM_LOG_ERROR("[%s][%d] fialed\n",__func__,__LINE__);
            return mod;
        }
    }
    return mod;
}

